#include "precompiled.hpp"
#include "pretty_print.hpp"
#include "detail/concatenation.hpp"
#include "scan.hpp"

struct block_increase
{
	int &inside_var_;
	block_increase(int &inside_var) : inside_var_(inside_var)
	{
		inside_var++;
	}
	~block_increase()
	{
		inside_var_--;
	}
};

pretty_print_visitor::pretty_print_visitor(std::ostream &output,const scan_context *scanner) : 
	output_(output), scanner_(scanner),cur_line_(1), inside_block_(), indent_()
{
}
pretty_print_visitor::~pretty_print_visitor()
{
}

void pretty_print_visitor::do_comments(const parse_node_base &node)
{
	if (node.line_ > cur_line_)
	{
		for (int f=cur_line_;f<node.line_;f++)
		{
			if (scanner_)
			{
				jam_string comment=scanner_->get_comment(f);
				if (!comment.empty())
					output_<<"#"<<comment;
			}
			output_<<std::endl;
		}
		cur_line_=node.line_;
	}
}

void pretty_print_visitor::print(parse_node_ptr node,bool do_indent)
{
	do_comments(*node);
	if (do_indent)
		for(int f=0;f<indent_;f++) output_<<"    ";
	node->visit(this);
}

void pretty_print_visitor::print(const jam_string &str,bool do_indent,
	const parse_node_base *node)
{
	if (node)
		do_comments(*node);
	if (do_indent)
		for(int f=0;f<indent_;f++) output_<<"    ";
	output_<<str;

	//It's safe to do this, because comments can't appear inside multi-string literals
	for(jam_string::const_iterator f=str.begin();f!=str.end();f++)
		if (*f=='\n') cur_line_++;
}

void pretty_print_visitor::visit_append_node(parse_node_base*,parse_node_ptr left_tree,
	parse_node_ptr right_tree)
{
	print(left_tree);
	print(right_tree);
}

void pretty_print_visitor::visit_eval_node(parse_node_base*,parse_node_ptr left_tree,
	parse_node_ptr right_tree, condition_type operation)
{
	bool is_unary=false;
	const char *sign=NULL;
	switch(operation)
	{
	case EXPR_NOT: // ! cond
		is_unary=true; sign="! ";
	break;	
	case EXPR_AND: // cond && cond
		is_unary=false; sign="&& ";
	break;
	case EXPR_OR:  // cond || cond
		is_unary=false; sign="|| ";
	break;
	case EXPR_EXISTS: // arg
		is_unary=true; sign="";
	break;
	case EXPR_EQUALS: // arg = arg
		is_unary=false; sign="= ";
	break;
	case EXPR_NOTEQ:	 // arg != arg
		is_unary=false; sign="!= ";
	break;
	case EXPR_LESS:	 // arg < arg
		is_unary=false; sign="< ";
	break;
	case EXPR_LESSEQ: // arg <= arg
		is_unary=false; sign="<= ";
	break;
	case EXPR_MORE:	 // arg > arg
		is_unary=false; sign="> ";
	break;
	case EXPR_MOREEQ: // arg >= arg
		is_unary=false; sign=">= ";
	break;
	case EXPR_IN:	 // arg in arg
		is_unary=false; sign="in ";
	break;
	default:
		assert(false); //@nc
	}

	assert(sign);
	block_increase guard(inside_block_);
	if (is_unary)
	{
		assert(is_null(right_tree));		
		output_<<sign;
		print(left_tree);
	}else
	{
		assert(!is_null(right_tree) && !is_null(left_tree));		
		print(left_tree);
		output_<<sign;
		print(right_tree);
	}
}

void pretty_print_visitor::visit_foreach_node(parse_node_base* node,parse_node_ptr var_values_tree,
	parse_node_ptr rule_tree, const jam_string& index_var_name, 
	bool is_var_local)
{	
	print("for ",true,node);
	if (is_var_local) print("local ");
	print(index_var_name+" in ");
	{
		block_increase guard(inside_block_);
		var_values_tree->visit(this);
	}	
//	print("{ ");
//	block_increase guard(indent_);
	print(rule_tree);
//	print("} ");
}

void pretty_print_visitor::visit_if_node(parse_node_base* node,parse_node_ptr condition_tree,
	parse_node_ptr then_tree, parse_node_ptr else_tree)
{
	print("if ",true,node);
	{
		block_increase guard(inside_block_);
		print(condition_tree);
	}	
	
//	print("{ ");
//	block_increase guard(indent_);
	print(then_tree);
//	print("} ");		

	if (!is_null(else_tree))
	{
		//block_increase guard(indent_);
		print("else ");
		print(else_tree);		
		//print("} ");
	}
}

void pretty_print_visitor::visit_include_node(parse_node_base* node,parse_node_ptr file_name_tree)
{
	print("include ",true,node);
	print(file_name_tree);
	print("; ");
}

void pretty_print_visitor::visit_list_node(parse_node_base* node,const jam_string &expand_string)
{
	//Check if we need quoting
	if (expand_string.find_first_of("\\\" #")==jam_string::npos && 
		!scan_context::is_keyword(expand_string))
		print(expand_string+" ",false,node);
	else
	{
		std::string res;
		res.reserve(expand_string.size()*2);
		for(size_t f=0;f<expand_string.size();f++)
		{
			char c=expand_string[f];
			if (c=='\\'||c=='\"')
				res.push_back('\\');
			res.push_back(c);
		}
		res='\"'+res+"\" ";
		print(res.c_str(),false,node);
	}
}

void pretty_print_visitor::visit_local_node(parse_node_base* node,parse_node_ptr var_list_tree, 
	parse_node_ptr val_list_tree, parse_node_ptr rule_list_tree)
{
	print("local ",true,node);
	print(var_list_tree);

	if (!is_null(val_list_tree))
	{
		print("= ");
		block_increase guard(inside_block_);
		print(val_list_tree);
	}
	print("; ");	
	print(rule_list_tree);
}

void pretty_print_visitor::visit_module_node(parse_node_base* node,parse_node_ptr module_name_tree, 
	parse_node_ptr module_content_tree)
{
	print("module ",true,node);
	print(module_name_tree);
//	print("{ ");
//	block_increase guard(indent_);
	print(module_content_tree);
//	print("} ");
}

void pretty_print_visitor::visit_class_node(parse_node_base* node,parse_node_ptr class_name_tree,
	parse_node_ptr right)
{
	print("class ",true,node);
	print(class_name_tree);
//	print("{ ");
//	block_increase guard(indent_);
	print(right);
//	print("} ");
}

void pretty_print_visitor::visit_null_node(parse_node_base*)
{
}

void pretty_print_visitor::visit_on_node(parse_node_base* node,parse_node_ptr list_tree,
	parse_node_ptr rule_tree)
{
	print("[ on ",false,node);	
	print(list_tree);
	print("return ");
	print(rule_tree);
	print("] ");
}

void pretty_print_visitor::visit_rule_node(parse_node_base* node,parse_node_ptr params_tree, 
	const jam_string &name)
{
	bool is_inside=inside_block_!=0;
	if (is_inside)
		print("[ ",false,node);
	print(name+" ",!is_inside,node);
	print(params_tree);
	if (is_inside)
		print("] ",false,node);
	else
		print("; ");
}

void pretty_print_visitor::visit_rules_node(parse_node_base*,parse_node_ptr rule_tree, 
	parse_node_ptr chained_rules_tree)
{
	rule_tree->visit(this);
	chained_rules_tree->visit(this);
}

const char* pretty_print_visitor::oper_to_string(assignment_operation operation)
{
	const char *sign=NULL;
	switch(operation)
	{
	case ASSIGN_SET:     //= assign variable
		sign="= ";
		break;
	case ASSIGN_APPEND:  //+= append variable
		sign="+= ";
		break;
	case ASSIGN_DEFAULT: //set only if unset
		sign="?= ";
		break;
	default:
		assert(false); //@nc
	}
	return sign;
}

void pretty_print_visitor::visit_set_node(parse_node_base*,parse_node_ptr var_names_tree,
	parse_node_ptr val_tree, assignment_operation operation)
{
	block_increase guard(inside_block_);
	print(var_names_tree,true);
	print(oper_to_string(operation));
	print(val_tree);
	print("; ");
}

void pretty_print_visitor::visit_settings_node(parse_node_base*,parse_node_ptr names_tree,
	parse_node_ptr target_names_tree,parse_node_ptr val_tree, 
	assignment_operation operation)
{
	print(names_tree,true);
	print("on ");
	print(target_names_tree);
	print(oper_to_string(operation));
	print(val_tree);
	print("; ");
}

void pretty_print_visitor::visit_setcomp_node(parse_node_base* node,parse_node_ptr rule_content_tree,
	parse_node_ptr param_list_tree, const jam_string &rule_name, 
	bool is_local)
{
	//TODO: add bracket_node. DONE!
	if (is_local) print("local ",true,node);
	print("rule "+rule_name+" ( ",!is_local,node);
	param_list_tree->visit(this);
	print(") ",false,node);
	
	print(rule_content_tree);	
}

jam_string pretty_print_visitor::flags_to_string(int flags)
{
	jam_string res;
	if (flags&EXEC_UPDATED)
		res+="updated ";		  
	if (flags&EXEC_TOGETHER)
		res+="together ";
	if (flags&EXEC_IGNORE)
		res+="ignore ";
	if (flags&EXEC_QUIETLY)
		res+="quietly ";
	if (flags&EXEC_PIECEMEAL)
		res+="piecemeal ";
	if (flags&EXEC_EXISTING)
		res+="existing ";

	return res;
}

void pretty_print_visitor::visit_setexec_node(parse_node_base* node,parse_node_ptr bind_var_tree,
	const jam_string &action_name, const jam_string &os_string, 
	int flags)
{
	print("actions ",true,node);
	print(flags_to_string(flags));
	print(action_name+" ");
	if (!is_null(bind_var_tree))
	{
		print("bind ");
		print(bind_var_tree);
	}
	print("{\n");
	print(os_string);
	print("} ");
}

void pretty_print_visitor::visit_switch_node(parse_node_base* node,parse_node_ptr value_tree,
	parse_node_ptr case_tree)
{
	print("switch ",true,node);
	{
		block_increase guard(inside_block_);
		print(value_tree);
	}
	print(case_tree);
}

void pretty_print_visitor::visit_case_node(parse_node_base* node,parse_node_ptr body_tree,
										   const jam_string &value)
{
	print("case "+value+" : ",true,node);
//	block_increase guard(indent_);
	print(body_tree);	
}

void pretty_print_visitor::visit_chain_node(parse_node_base*,parse_node_ptr left_tree,
	parse_node_ptr right_tree)
{
	if (left_tree) left_tree->visit(this);
	if (right_tree) right_tree->visit(this);
}

void pretty_print_visitor::visit_while_node(parse_node_base* node,parse_node_ptr condition_tree,
	parse_node_ptr body_tree)
{
	print("while ",true,node);
	{
		block_increase guard(inside_block_);
		print(condition_tree);
	}
//	print("{ ");
//	block_increase guard(indent_);
	print(body_tree);
//	print("} ");
}

void pretty_print_visitor::visit_lol_node(parse_node_base* node,parse_node_ptr list_elem,
	parse_node_ptr chained_list)
{
	block_increase guard(inside_block_);
	print(list_elem);
	if (chained_list)
	{
		print(" : ");		
		print(chained_list);
	}
}

void pretty_print_visitor::visit_return_node(parse_node_base* node, parse_node_ptr list_res)
{
	print("return ",true,node);
	block_increase guard(inside_block_);
	print(list_res);
	print("; ");
}

void pretty_print_visitor::visit_left_brace_node(parse_node_base *node, 
	parse_node_ptr body_tree, parse_node_ptr rbracket_tree)
{
	print("{ ",true,node);
	{
		block_increase guard(indent_);
		print(body_tree);
	}
	print(rbracket_tree);
}

void pretty_print_visitor::visit_right_brace_node(parse_node_base* node)
{
	print("} ",true,node);
}
